/*
 * C4.6.1.c
 *
 *  Created on: 2012/08/24
 *      Author: owner
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"

static const PSZ RuleID = "C4.6.1";

#define NG_KIND_OVER	1
#define NG_KIND_MINUS	2
#define NG_KIND_SIGNED	3

static const PSZ MESSAGE_ENG = "'%s' Operator's right operand is %s";
static const PSZ MESSAGE1_ENG = "too big";
static const PSZ MESSAGE2_ENG = "minus";
static const PSZ MESSAGE3_ENG = "signed type";

static const PSZ MESSAGE_JPN = "'%s'演算子の右側オペランドは%s";
static const PSZ MESSAGE1_JPN = "シフト幅を超える";
static const PSZ MESSAGE2_JPN = "負数";
static const PSZ MESSAGE3_JPN = "符号付きの型";

static const PSZ MESSAGE_CHS = "'%s'演算子的右操作数%s";
static const PSZ MESSAGE1_CHS = "过大";
static const PSZ MESSAGE2_CHS = "是负数";
static const PSZ MESSAGE3_CHS = "是带符号的型";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

/******************************************************************
 * static function:
 * checkNGKind
 *******************************************************************/
static INT checkNGKind(AST cmdIndex) {

	INT ngKind = 0;

	AST leftOpIndex = 0;
	AST leftOpcmd = 0;
	AST leftTypeIndex = 0;
	U8 leftBit = 0;

	AST rightOpIndex = 0;
	AST rightOpCmd = 0;
	AST rightTypeIndex = 0;
	U8 rightBit = 0;
	AST rightValueIndex = 0;

	rightOpIndex = LinkCommandList.getRightOperand(cmdIndex);
	rightOpCmd = LinkOperand.getParameter(rightOpIndex);

	if (LinkCommandList.isGetOperandValue(rightOpCmd)) {

		if (LinkOperand.isConstant(
				LinkCommandList.getLeftOperand(rightOpCmd))) {

			rightValueIndex = LinkOperand.getParameter(
					LinkCommandList.getLeftOperand(rightOpCmd));

			if (!LinkConstant.isFloating(rightValueIndex)) {

				leftOpIndex = LinkCommandList.getLeftOperand(cmdIndex);
				leftOpcmd = LinkOperand.getParameter(leftOpIndex);
				leftTypeIndex = LinkCommandList.getType(leftOpcmd);
				if (LinkTypeInfo.isBasicType(leftTypeIndex)) {
					if (LinkConstant.isNegative(rightValueIndex)) {
						ngKind = NG_KIND_MINUS;
					} else {
						leftBit = LinkTypeInfo.getTypeSize(leftTypeIndex) << 3;
						rightBit =
								LinkConstant.getInteger(rightValueIndex).u8Remainder;
						if (rightBit >= leftBit) {
							ngKind = NG_KIND_OVER;
						}
					}
				}
			}
		} else {
			rightTypeIndex = LinkCommandList.getType(rightOpCmd);
			if (LinkTypeInfo.isSigned(rightTypeIndex)) {
				ngKind = NG_KIND_SIGNED;
			}
		}
	} else {
		rightTypeIndex = LinkCommandList.getType(rightOpCmd);
		if (LinkTypeInfo.isSigned(rightTypeIndex)) {
			ngKind = NG_KIND_SIGNED;
		}
	}

	return ngKind;
}

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(AST cmdIndex, U4 event) {

	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;
	PSZ opName = NULL;

	if (!(event & EVENT_CMDLIST_PREV)) {
		return EXIT_SUCCESS;
	}

	if (!LinkCommandList.isLeftShift(cmdIndex)
			&& !LinkCommandList.isRightShift(cmdIndex)) {
		return EXIT_SUCCESS;
	}

	/*check logic*/
	/*<<*/
	if (LinkCommandList.isLeftShift(cmdIndex)) {
		opName = "&lt;&lt;";
		result = checkNGKind(cmdIndex);
	}
	/*>>*/
	else if (LinkCommandList.isRightShift(cmdIndex)) {
		opName = ">>";
		result = checkNGKind(cmdIndex);
	}

	if (result == RULE_RESULT_OK) {
		return EXIT_SUCCESS;
	}

	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == cmdIndex) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		newNode->indexInfo = cmdIndex;
		newNode->fileIndex = CCodeScope.getFileIndex(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->beginLine = CCodeScope.getBeginLine(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->beginCol = CCodeScope.getBeginLineOffset(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->endLine = CCodeScope.getEndLine(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->endCol = CCodeScope.getEndLineOffset(
				LinkCommandList.getCodeScope(cmdIndex));
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		if (result == NG_KIND_OVER) {
			sprintf(newNode->msgBuf_eng, MESSAGE_ENG, opName, MESSAGE1_ENG);
			sprintf(newNode->msgBuf_chs, MESSAGE_CHS, opName, MESSAGE1_CHS);
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN, opName, MESSAGE1_JPN);
		} else if (result == NG_KIND_MINUS) {
			sprintf(newNode->msgBuf_eng, MESSAGE_ENG, opName, MESSAGE2_ENG);
			sprintf(newNode->msgBuf_chs, MESSAGE_CHS, opName, MESSAGE2_CHS);
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN, opName, MESSAGE2_JPN);
		} else if (result == NG_KIND_SIGNED) {
			sprintf(newNode->msgBuf_eng, MESSAGE_ENG, opName, MESSAGE3_ENG);
			sprintf(newNode->msgBuf_chs, MESSAGE_CHS, opName, MESSAGE3_CHS);
			sprintf(newNode->msgBuf_jpn, MESSAGE_JPN, opName, MESSAGE3_JPN);
		}

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C4_6_1_register(VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_cmd = RuleMain;
	newRuleMan->ruleMain_ast = NULL;
	newRuleMan->nextRule = NULL;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}
